#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/io.h>
#include <linux/string.h>

#include "led_operation.h"
#include "led_driver.h"

#define DEVICE_NAME "led_drv"

static int major = 0;
static struct class *led_class;
struct led_operations *p_led_opr;
static int major;
static struct class *led_class;

void led_class_create_device(int minor)
{
	device_create(led_class, NULL, MKDEV(major, minor), NULL, "%s%d", DEVICE_NAME, minor); /* /dev/100ask_led0,1,... */
    printk(KERN_INFO "device leddrv%d created!\n", minor);
}
void led_class_destroy_device(int minor)
{
	device_destroy(led_class, MKDEV(major, minor));
}
void register_led_operations(struct led_operations *opr)
{
	p_led_opr = opr;
}

EXPORT_SYMBOL(led_class_create_device);
EXPORT_SYMBOL(led_class_destroy_device);
EXPORT_SYMBOL(register_led_operations);

ssize_t led_read(struct file *fd, char __user *buf, 
    size_t size, loff_t *lof){
        return 0;
}

ssize_t led_write(struct file *fd, const char __user *buf, 
    size_t size, loff_t *lof){
    int err;
	char status;
	struct inode *inode = file_inode(fd);
	int minor = iminor(inode);
	
	err = copy_from_user(&status, buf, 1);

	p_led_opr->ctrl(minor, status);
	
	return 1;
}

int led_open(struct inode *node, struct file *fd){
    int minor = iminor(node);
	p_led_opr->init(minor);
    printk(KERN_INFO "led driver initialized\n");
    return 0;
}

int led_close(struct inode *node, struct file *fd){
    return 0;
}

static struct file_operations led_fop = {
    .owner   = THIS_MODULE,
	.read    = led_read,
	.write   = led_write,
    .open    = led_open,
    .release = led_close,
};

static int __init led_init(void) {
    int ret = 0;

    major = register_chrdev(0, DEVICE_NAME, &led_fop);
    if (major < 0) {
        ret = major;
        printk(KERN_ERR "led_driver: Failed to register char device, error %d\n", ret);
        return ret;
    }
    
    led_class = class_create(THIS_MODULE, DEVICE_NAME);
    if (IS_ERR(led_class)) {
        ret = PTR_ERR(led_class);
        printk(KERN_ERR "led_driver: Failed to create class, error %d\n", ret);
        unregister_chrdev(major, DEVICE_NAME);
        return ret;
    }

    return 0;
}

static void __exit led_exit(void){
    class_destroy(led_class);
    unregister_chrdev(major, DEVICE_NAME);

    printk(KERN_INFO "device %s(%d) unregisted\n", DEVICE_NAME, major);
}

module_init(led_init);
module_exit(led_exit);

MODULE_AUTHOR("fison");
MODULE_DESCRIPTION("hello");
MODULE_LICENSE("GPL");